import 'dart:async';
import 'dart:convert';
import 'dart:math';

import 'package:get/get_rx/src/rx_types/rx_types.dart';
import 'package:orginone/dart/extension/index.dart';
import 'package:orginone/dart/core/public/consts.dart';
import 'package:orginone/dart/core/target/outTeam/cohort.dart';
import 'package:orginone/utils/system/notify/notification_util.dart';
import 'package:orginone/dart/base/common/commands.dart';
import 'package:orginone/dart/base/common/format.dart';
import 'package:orginone/dart/core/public/entity.dart';
import 'package:orginone/dart/core/public/enums.dart';
import 'package:orginone/dart/base/schema.dart';
import 'package:orginone/dart/core/target/base/target.dart';
import 'package:orginone/dart/base/model.dart';
import 'package:orginone/dart/core/public/collection.dart';
import 'package:orginone/dart/core/chat/message.dart';
import 'package:orginone/dart/core/chat/activity.dart';
import 'package:orginone/main.dart';
import 'package:orginone/utils/log/log_util.dart';
import 'package:orginone/utils/system/system_utils.dart';

import '../target/team/company.dart';

// 空时间

var nullTime = DateTime.parse('2022-07-01');
// // 消息变更推送
// var msgChatNotify = Emitter();

/// 会话接口类
abstract class ISession extends IEntity<XEntity> {
  /// 是否归属人员
  late bool isBelongPerson;

  /// 成员是否有我
  late bool isMyChat;

  /// 是否是好友
  late bool isFriend;

  /// 会话id
  late String sessionId;

  /// 会话的用户
  late ITarget target;

  /// 会话主体元数据
  late MsgChatData chatdata;

  /// 未读消息数量
  late int noReadCount;

  /// 会话描述
  late String information;

  /// 会话的历史消息
  late List<IMessage> messages;

  /// 是否为群会话
  late bool isGroup;

  /// 会话的成员
  late List<XTarget> members;

  /// 会话的成员数量
  late int memberCount;

  /// 会话动态
  late IActivity activity;

  /// 是否可以删除消息
  late bool canDeleteMessage;

  /// 是否初始化加载
  bool isLoaded = false;

  /// 加载更多历史消息
  Future<int> moreMessage();

  /// 禁用通知
  void unMessage();

  /// 黑名单列表
  late List blackList;

  /// 消息变更通知
  Future<void> onMessage(Function(List<IMessage> messages)? callback);

  /// 向会话发送消息
  Future<bool> sendMessage(
    MessageType type,
    String text,
    List<String> mentions, {
    IMessage? cite,
    List<IMessage>? forward,
    Future<void> Function(IMessage msg)? uploadTaskCallback,
  });

  /// 重新发送消息
  Future<bool> resendMessage(IMessage message);

  /// 撤回消息
  Future<void> recallMessage(String id);

  Future<void> tagMessage(List<String> ids, String tag);

  /// 删除消息
  Future<bool> deleteMessage(String id);

  /// 清空历史记录
  Future<bool> clearMessage();

  /// 通知
  void emitter(String cmd, [dynamic args]);

  // /// 清空缓存
  // void clearCache();

  /// 缓存会话数据
  Future<bool> cacheChatData([bool? notify]);

  /// 更新最新消息
  Future<void> updateMessage({Map<String, dynamic>? chatData});
}

/// 会话实现
class Session extends Entity<XEntity> implements ISession {
  Session(this.sessionId, this.target, this.metadata, {this.tags})
      : super(metadata, tags ?? []) {
    sessionId = id;
    if (tags == null) {
      tags = [metadata.typeName!];
      if (metadata.belong != null) {
        tags?.insert(0, metadata.belong!.name ?? "");
      }
    }
    // XLogUtil.dd('>>>>>> members $name session ${target.hashCode}');
    chatdata = MsgChatData(
      fullId: '${target.id}_$id',
      chatName: metadata.name ?? '',
      chatRemark: metadata.remark ?? '',
      isToping: false,
      noReadCount: 0,
      lastMsgTime: nullTime.millisecondsSinceEpoch,
      mentionMe: false,
      labels: [],
      lastMessage: null,
      recently: false,
    );
    // members = <XTarget>[].obs;
    messages = <IMessage>[].obs;
    activity = Activity(metadata, this);
    noReadCount = 0;
    newMessageHandler = NewMessageHandler(this);
    // loadCacheChatData();
    // if (isCohort && null != kernel.user) {
    //   TargetResource.pullMembers(id, [kernel.user!]);
    // }
    Future.delayed(Duration(milliseconds: id == userId ? 100 : 0), () async {
      await _loadCacheChatData();
      // await activity.load();
    });
    // if (id != userId) {
    //   loadCacheChatData();
    // }
    designateId =
        target.typeName == TargetType.group ? target.space?.id ?? "" : userId;
  }

  @override
  String sessionId;
  @override
  final ITarget target;
  @override
  final XTarget metadata;

  List<String>? tags;

  /// 消息类会话元数据
  @override
  late MsgChatData chatdata;
  late String designateId;

  /// 未读消息数量
  @override
  late int noReadCount;

  /// 新消息处理器（未读新消息）
  late NewMessageHandler newMessageHandler;

  /// 会话的历史消息
  @override
  late List<IMessage> messages;

  /// 会话的成员
  // @override
  // late RxList<XTarget> members;
  @override
  late IActivity activity;
  // @override
  // RxList<IMessage> messages = [];
  Function(List<IMessage> messages)? messageNotify;

  /// 判断是否初始化
  @override
  bool isLoaded = false;

  /// 每页条数
  int pageSize = 20;
  int skip = 0;

  XCollection<ChatMessageType> get coll {
    return target.resource.messageColl;
  }

  @override
  List<XTarget> get members {
    return isGroup ? target.members : [];
  }

  @override
  int get memberCount {
    return isGroup ? target.memberCount : 0;
  }

  @override
  bool get isGroup {
    return target.id == sessionId && sessionId != userId;
  }

  bool get isCohort {
    return target.id == sessionId && sessionId != userId && target is Cohort;
  }

  dynamic get sessionMatch {
    return isGroup
        ? {"toId": sessionId, "isDeleted": false}
        : {
            "isDeleted": false,
            "_or_": [
              {"fromId": sessionId, "toId": userId},
              {"fromId": userId, "toId": sessionId},
            ],
          };
  }

  @override
  bool get isBelongPerson {
    return (metadata.belongId == metadata.createUser &&
            target is! ICompany // !('stations' in this.target)
        );
  }

  @override
  bool get isMyChat {
    // return (metadata.typeName == TargetType.person.label ||
    //     members.any((i) => i.id == userId) ||
    //     chatdata.noReadCount > 0);
    var hasAuth = false;
    if (target.typeName == TargetType.group.label) {
      var auths = [OrgAuth.relationAuthId.label, OrgAuth.superAuthId.label];
      if (target.space?.superAuth != null) {
        auths.addAll(target.space?.superAuth
                ?.findAuthByOrgId(target.id)
                .map((a) => a.id) ??
            []);
      }
      hasAuth = target.user?.authenticate(
            null != target.space ? [target.id, target.space!.id] : [target.id],
            auths,
          ) ??
          false;
    }
    return (metadata.typeName == TargetType.person.label ||
        metadata.typeName == TargetType.storage.label ||
        members.any((i) => i.id == userId) ||
        (metadata.typeName == TargetType.group.label && hasAuth) ||
        chatdata.noReadCount > 0);
  }

  @override
  bool get isFriend {
    return (metadata.typeName != TargetType.person.label ||
        target.user!.members.any((i) => i.id == sessionId));
  }

  @override
  String get remark {
    if (null != chatdata.lastMessage) {
      var msg = Message(chatdata.lastMessage!, this);
      return msg.msgTitle;
    }
    return metadata.remark?.substring(0, min(15, metadata.remark!.length)) ??
        "";
  }

  @override
  String get updateTime {
    if (chatdata.lastMessage != null) {
      return chatdata.lastMessage?.createTime ?? "";
    }
    return super.updateTime;
  }

  String? get copyId {
    if (target.id == userId && sessionId != userId) {
      return sessionId;
    }
    return null;
  }

  @override
  List<String> get groupTags {
    var gtags = [...super.groupTags];
    if (companyTypes.includes(TargetType.getType(typeName))) {
      // gtags.add('单位');
    } else {
      gtags.add(typeName);
    }
    if (id == userId) {
      gtags.add('本人');
    } else if (isGroup) {
      if (target.space?.id != userId) {
        gtags.add(target.space!.name);
      } else {
        gtags.add(belong.name);
      }
      gtags.addAll(target.groupTags);
      // gtags.add(typeName);
    }
    if (chatdata.noReadCount > 0) {
      gtags.add('未读');
    }
    if (chatdata.mentionMe) {
      gtags.add('@我');
    }
    if (chatdata.isToping) {
      gtags.add('常用');
    }
    return [...gtags, ...chatdata.labels].toList().toSet().toList();
  }

  @override
  String get information {
    if (chatdata.lastMessage != null) {
      var msg = Message(chatdata.lastMessage!, this);
      return msg.msgTitle;
    }
    return metadata.remark!.substring(0, min(20, metadata.remark!.length));
  }

  String get cachePath {
    return 'session.${chatdata.fullId}';
  }

  @override
  bool get canDeleteMessage {
    return target.id == userId || target.hasRelationAuth();
  }

  @override
  Future<int> moreMessage() async {
    skip += pageSize;
    var data = await coll.loadSpace({
      "take": pageSize,
      "skip": skip - pageSize,
      "options": {
        "match": sessionMatch,
        "sort": {
          "createTime": -1,
        },
      },
    }, ChatMessageType.fromJson);
    if (data.isNotEmpty) {
      if (skip == pageSize) {
        clearCache();
      }
      for (var msg in data) {
        newMessageHandler.put(Message(msg, this));
      }
      readMessages(messages);
      if (chatdata.lastMsgTime == nullTime) {
        chatdata.lastMessage = data[0];
        chatdata.lastMsgTime =
            DateTime.parse(data[0].createTime!).millisecondsSinceEpoch;
        // if (reload) {
        //   await target.user?.cacheObj.all(reload: reload);
        //   await loadCacheChatData();
        // }
      }
      if (skip > pageSize) {
        emitter('new');
      }
      return data.length;
    }
    return 0;
  }

  /// 通知
  @override
  void emitter(String cmd, [dynamic args]) {
    command.emitter("session-${chatdata.fullId}", cmd);
  }

  @override
  void unMessage() {
    messageNotify = null;
    XLogUtil.dd("当前会话:销毁");
  }

  @override
  Future<void> onMessage(Function(List<IMessage> messages)? callback) async {
    // 判断是否有缓存，有缓存就不执行
    messageNotify = callback;
    XLogUtil.dd("当前会话:注册");
    if (isLoaded) {
      Future.delayed(
          const Duration(milliseconds: 100), () => readMessages(messages));
      return;
    }

    clearCache();
    isLoaded = true;
    skip = 0;
    await moreMessage();
  }

  void readMessages(List<IMessage> messages, [Message? msg]) {
    var readCount = 0;
    if (null != msg) {
      // if (null != messageNotify) return;

      var index = messages.indexWhere((i) => i.id == msg.id);
      if (index > -1) {
        messages[index] = msg;
        readCount = msg.isMySend ? 0 : 1;
      }
    } else {
      var ids = messages
          .where((i) {
            return !i.isReaded;
          })
          .map((i) => i.id)
          .toList();
      if (ids.isNotEmpty) {
        tagMessage(ids, '已读');
        readCount = chatdata.noReadCount;
        // readCount = chatdata.noReadCount > pageSize
        //     ? pageSize
        //     : chatdata.noReadCount;
        XLogUtil.dd('>>>>>>>====已读数：$readCount');
      } else {
        readCount = chatdata.noReadCount;
      }
    }

    chatdata.mentionMe = false;
    // if (readCount > 0) {
    //   chatdata.noReadCount -= min(readCount, chatdata.noReadCount);
    //   cacheChatData(true);
    //   refreshNoReadCount();
    // }
    if (chatdata.noReadCount > 0) {
      chatdata.noReadCount = 0;
      cacheChatData(true);
      refreshNoReadCount();
      notification();
    }
    // LogUtil.d('>>>>>>>======readMessages $readCount $msg ${messages.length}');
    // if (readCount > 0 && null != msg) {
    // notification();
    // }
  }

  /// 重新发送消息
  @override
  Future<bool> resendMessage(IMessage imsg) async {
    imsg.sendState = ExecuteStatus.starting;
    if (null != imsg.uploadFile) {
      await imsg.uploadFile?.call(imsg);
    }
    _sendMessageAsync(imsg.metadata).then((value) {
      if (value) {
        imsg.sendState = ExecuteStatus.success;
      } else {
        imsg.sendState = ExecuteStatus.failed;
      }
      imsg.changeCallback();
    });
    return true;
  }

  @override
  Future<bool> sendMessage(
    MessageType type,
    String text,
    List<String> mentions, {
    IMessage? cite,
    List<IMessage>? forward,
    Future<void> Function(IMessage msg)? uploadTaskCallback,
  }) async {
    if (cite != null) {
      cite.metadata.comments = [];
    }
    if (forward != null && forward.isNotEmpty) {
      forward = forward.map((e) {
        e.metadata.comments = [];
        return e;
      }).toList();
    }

    var createTime = DateTime.now().format(format: "yyyy-MM-dd HH:mm:ss");
    ChatMessageType msgData = ChatMessageType.fromJson({
      "typeName": type.label,
      "fromId": userId,
      "toId": sessionId,
      "createTime": createTime,
      "createUser": userId,
      "updateUser": userId,
      "updateTime": createTime,
      "comments": [],
      "designateId": designateId,
      "deviceId": SystemUtils.getDeviceId(),
      "content": StringGzip.deflate(
        '[obj]${json.encode({
              "body": text,
              "mentions": mentions,
              "cite": cite?.metadata,
              'forward': forward?.map((e) => e.metadata).toList()
            })}',
      ),
    });
    var imsg = Message(msgData, this);
    imsg.uploadFile = uploadTaskCallback;
    resendMessage(imsg);
    newMessage(imsg.metadata, imsg);
    return true;
  }

  Future<bool> _sendMessageAsync(
    ChatMessageType msgData,
  ) async {
    // LogUtil.d(
    //     '>>>==========================================================================');
    XLogUtil.d(
        '>>>KEY:$key ID:$id hashCode:$hashCode belong:$belongId target:${target.id} name:$name');
    // LogUtil.d(
    //     '>>>^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^');
    var data = await coll.insert(msgData,
        fromJson: ChatMessageType.fromJson, copyId: copyId);
    if (data != null) {
      msgData.id = data.id;
      await notify('insert', [data], false);
    }
    return data != null;
  }

  @override
  Future<void> recallMessage(String id) async {
    var data = await coll.update(
        id,
        {
          "_set_": {"typeName": MessageType.recall.label},
        },
        copyId,
        ChatMessageType.fromJson);
    if (data != null) {
      await notify('replace', [data]);
    }
  }

  @override
  Future<void> tagMessage(List<String> ids, String tag) async {
    var data = await coll.updateMany(
        ids,
        {
          "_push_": {
            "comments": {
              "label": tag,
              "time": 'sysdate()',
              "userId": userId,
              "designateId": designateId,
            },
          },
        },
        copyId,
        ChatMessageType.fromJson);
    if (data != null && data.isNotEmpty) {
      await notify('replace', data);
    }
  }

  @override
  Future<bool> deleteMessage(String id) async {
    if (canDeleteMessage) {
      for (var item in messages) {
        if (item.id == id) {
          if (await coll.delete(item.metadata)) {
            var index = messages.indexWhere((i) => i.id == id);
            if (index > -1) {
              messages.removeAt(index);
            }
            chatdata.lastMsgTime = DateTime.now().millisecondsSinceEpoch;
            messageNotify?.call(messages);
            return true;
          }
        }
      }
    }
    return false;
  }

  @override
  Future<bool> clearMessage() async {
    if (canDeleteMessage) {
      var success = await coll.deleteMatch(sessionMatch);
      if (success) {
        messages.clear();
        chatdata.lastMsgTime = DateTime.now().millisecondsSinceEpoch;
        messageNotify?.call(messages);
        sendMessage(MessageType.notify, '${target.user?.name} 清空了消息', []);
        return true;
      }
    }
    return false;
  }

  /// 刷新消息
  @override
  Future<void> updateMessage({Map<String, dynamic>? chatData}) async {
    if (null != chatData) {
      target.user?.cacheObj.setValue(cachePath, chatData);
    }
    await loadCacheChatData();
    if (null != messageNotify) {
      isLoaded = false;
      onMessage((messages) => null);
    } else {
      isLoaded = false;
      clearCache();
    }
  }

  /// 清空缓存数据
  void clearCache() {
    // isLoaded = false;
    messages.clear();
    newMessageHandler.clear();
    // LogUtil.d('>>>>>>======clear');
  }

  Future<bool> notify(
    String operate,
    List<ChatMessageType> data, [
    bool onlineOnly = true,
  ]) async {
    return await coll.notity(
      {
        "data": data,
        "operate": operate,
      },
      ignoreSelf: false,
      targetId: sessionId,
      onlyTarget: true,
      onlineOnly: onlineOnly,
    );
  }

  Future<void> _loadCacheChatData() async {
    await loadCacheChatData();
    target.user?.cacheObj.subscribe(chatdata.fullId, (data) {
      data = MsgChatData.fromJson(data);
      if (data.fullId == chatdata.fullId) {
        chatdata = data as MsgChatData;
        refreshNoReadCount();
        target.user?.cacheObj.setValue(cachePath, data);
        // command.emitterFlag('session-${data.fullId}', [data.noReadCount]);
        command.emitterFlag('session');
      }
    });
    // LogUtil.d('>>>>=======$key');
    _subscribeMessage();
  }

  Future<void> loadCacheChatData() async {
    // target.user?.cacheObj.clear();
    var data = await target.user?.cacheObj
        .get<MsgChatData>(cachePath, MsgChatData.fromJson);
    if (data?.fullId == chatdata.fullId) {
      chatdata = data!;
      // LogUtil.dd('>>>>>>======loadCacheChatData ${data.toJson()}');
      refreshNoReadCount();
      changeCallback();
    }
  }

  @override
  Future<bool> cacheChatData([bool? notify = false]) async {
    if (relationCtrl.appStartController.hasDataDelay) return false;
    // ToastUtils.showMsg(
    //     msg:
    //         "更新会话未读数：${relationCtrl.appStartController.appStartStatus.name} ${chatdata.noReadCount}");
    var success = await target.user?.cacheObj.set(cachePath, chatdata);
    if (success! && notify!) {
      await target.user?.cacheObj.notity(
        chatdata.fullId,
        chatdata,
        onlyTarget: true,
        ignoreSelf: true,
      );
    }
    return success;
  }

  void notification([bool mobilePush = false, IMessage? msg]) {
    XLogUtil.dd('>>>>>消息变化通知 ${chatdata.noReadCount} $noReadCount $hashCode');
    command.emitterFlag('session');
    // command.emitterFlag('session-${chatdata.fullId}', [chatdata.noReadCount]);

    changeCallback();
    messageNotify?.call(messages);

    if (mobilePush && null != msg) {
      NotificationUtil.showChatMessageNotification(this, msg);
    }
  }

  void addMessage(ChatMessageType data, IMessage imsg) {
    newMessageHandler.put(imsg, 0);
    chatdata.lastMsgTime = DateTime.now().millisecondsSinceEpoch;
    chatdata.lastMessage = data;
  }

  void newMessage(ChatMessageType data, IMessage msg) {
    if (_isNoValidNewMessage(msg)) {
      return;
    }

    List blackList = relationCtrl.user?.blackList ?? [];
    if (blackList.contains(data.fromId)) {
      ///已拉黑对方 不接收新消息
      ///本条消息设置不展示
      tagMessage([msg.id], '隐藏');

      ///通知对方消息被拒收
      sendMessage(MessageType.notify, '消息已发出，但被对方拒收了。', []);
      return;
    }
    addMessage(data, msg);
    XLogUtil.dd("新消息：${chatdata.noReadCount} ${messageNotify == null}");
    if (messageNotify == null) {
      chatdata.noReadCount += msg.isMySend ? 0 : 1;
      refreshNoReadCount();
      if (!chatdata.mentionMe) {
        chatdata.mentionMe = msg.mentions.contains(userId);
      }
      notification(userId != data.fromId, msg);
    } else if (!msg.isReaded) {
      tagMessage([msg.id], '已读');
    }
    cacheChatData(messageNotify != null && !msg.isMySend);
    // command.emitter('session-${chatdata.fullId}', 'insert');
    emitter('new');
  }

  bool _isNoValidNewMessage(IMessage msg) {
    bool res = newMessageHandler.hasNoReadMessage(msg);
    if (msg.sendState == ExecuteStatus.starting) {
      return res;
    } else if (msg.metadata.deviceId != SystemUtils.getDeviceId()) {
      return res;
    }
    return true;
  }

  /// 接收推送消息
  void receiveMessage(String operate, ChatMessageType data) {
    var imsg = Message(data, this);
    XLogUtil.d(
        '>>>==========================================================================');
    XLogUtil.d(
        '>>>KEY:$key ID:$id hashCode:$hashCode belong:$belongId target:${target.id} name:$name msgBody ${imsg.msgBody}');
    XLogUtil.d(
        '>>>^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^');
    if (operate == 'insert') {
      newMessage(data, imsg);
    } else {
      // readMessages(messages, imsg);
      var index = messages.indexWhere((i) => i.id == data.id);
      if (index > -1) {
        messages[index] = imsg;
        emitter('new');
      }
      if (data.id == chatdata.lastMessage?.id) {
        chatdata.lastMessage = data;
        cacheChatData(messageNotify != null && !imsg.isMySend);
      }
    }
  }

  void refreshNoReadCount() {
    noReadCount = chatdata.noReadCount;
    // if (chatdata.noReadCount > 0) {
    //   noReadCount =
    //       chatdata.noReadCount > 99 ? "99+" : chatdata.noReadCount.toString();
    // } else {
    //   noReadCount = '';
    // }
  }

  void _subscribeMessage() {
    if (isGroup) {
      coll.unsubscribe();
      coll.subscribe(
        [key],
        (res) => {
          res['data'].forEach((item) =>
              receiveMessage(res['operate'], ChatMessageType.fromJson(item)))
        },
      );
    } else {
      coll.unsubscribe(id: sessionId);
      coll.subscribe(
        [key],
        (res) => {
          res['data'].forEach((item) => {
                if ([item['fromId'], item['toId']].contains(sessionId) &&
                    [item['fromId'], item['toId']].contains(userId))
                  {
                    receiveMessage(
                        res['operate'], ChatMessageType.fromJson(item))
                  }
              })
        },
        sessionId,
      );
    }
  }

  // Future<void> subscribeOperations() async {
  //   if (isGroup) {
  //     coll.subscribe(
  //       [key],
  //       ({required String operate, required List<ChatMessageType> data}) {
  //         data.map((item) => receiveMessage(operate, item));
  //       },
  //     );
  //   } else {
  //     coll.subscribe(
  //       [key],
  //       ({required String operate, required List<ChatMessageType> data}) {
  //         for (var item in data) {
  //           if ([item.fromId, item.toId].contains(sessionId) &&
  //               [item.fromId, item.toId].contains(userId)) {
  //             receiveMessage(operate, item);
  //           }
  //         }
  //       },
  //       sessionId,
  //     );
  //   }
  // }

  @override
  dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}

/// 新消息处理器
class NewMessageHandler {
  Session session;

  // 未读消息集合
  List<String> noReadMessageIds = [];

  NewMessageHandler(this.session);

  List<IMessage> get messages => session.messages;

  /// 添加消息
  void put(IMessage message, [int? index]) {
    if (null != index) {
      messages.insert(index, message);
    } else {
      hasNoReadMessage(message);
      messages.add(message);
    }
  }

  bool hasNoReadMessage(IMessage msg) {
    if (!msg.isReaded) {
      if (noReadMessageIds.contains(msg.id)) {
        updateMessage(msg);
        noReadMessageIds.remove(msg.id);
        // LogUtil.d('>>>>>=====remove ${msg.id}');
        return true;
      } else {
        noReadMessageIds.add(msg.id);
        // LogUtil.d('>>>>>=====add ${msg.id}');
        return false;
      }
    } else {
      return false;
    }
  }

  // 更新消息信息
  void updateMessage(IMessage msg) {
    for (var element in messages) {
      if (element.id == msg.id) {
        if (element.isReaded != msg.isReaded) {
          element.isReaded = msg.isReaded;
        }
        break;
      }
    }
  }

  /// 判断是否有消息内容
  bool hasMessage() {
    return session.messages.isNotEmpty;
  }

  void clear() {
    noReadMessageIds.clear();
  }
}
